/////////////////////////////////////////////////////////////////////////////
// Purpose:     Smart management of dynamic objects
// Author:      Jesus Gonzalez
// Modified by: 
// Copyright:   (c) 2003-2013 Jesus Gonzalez <jgonzalez@gdr-sistemas.com>
// License:     BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _SMARTLIB_SMARTPTR_H_
#define _SMARTLIB_SMARTPTR_H_

/**
 * \file
 * Smart management of dynamic objects.
 */

#include <stddef.h>

///@defgroup dyn_mngt Dynamic Object Management
///@{

///@cond INTERNAL
#ifndef _SMARTPTR_DEBUG_
/// If different from 0, enables smart pointer debugging features.
#define _SMARTPTR_DEBUG_ 0
#endif
///@endcond

/**
 * Smart pointer to class declaration.
 *
 * This macro is used inside the declaration of a class derived from SmartObject
 * to define \a Ptr and \a ConstPtr as typedefs of smart pointers to that class instances.
 *
 * @param[in] cls Class name
 */
#define DECLARE_SMARTPTR(cls) \
/** Type definition for a smart pointer to non-constant instances of this class */ \
typedef SmartPtr<cls> Ptr; \
/** Type definition for a smart pointer to constant instances of this class */ \
typedef SmartPtr<const cls> ConstPtr;

/**
 * Smart pointer to class and default constructor declaration.
 *
 * This macro is used inside the declaration of a class derived from SmartObject
 * to define \a Ptr and \a ConstPtr as typedefs of smart pointers to that class instances.
 *
 * Additionally, a \a New() constructor method is defined that creates a default instance
 * of the class and returns a smart pointer to it.
 *
 * @param[in] cls Class name
 */
#define DECLARE_SMARTPTR_NEWCTOR(cls) \
DECLARE_SMARTPTR(cls) \
/** Creates a new instance of this class and returns a smart pointer to it */ \
static cls::Ptr New() { return new cls(); }

/**
 * The SmartObject abstract class is the base class for all classes whose instances can be
 * dynamically managed using @link SmartPtr Smart Pointers@endlink.
 *
 * Useful macros:
 *  - #DECLARE_SMARTPTR
 *  - #DECLARE_SMARTPTR_NEWCTOR
 *
 * @remark
 * Smart objects can be created on the heap or on the stack, but *only* the ones created on
 * the heap (i.e. using the operator \a new) can be dynamically managed using smart pointers.
 */
class SmartObject
{
private:
#if defined(_DEBUG_) && _SMARTPTR_DEBUG_ == 1
    void Reference();
    unsigned int Unreference();
#else
	void Reference()
	{
		references++;
	}
	unsigned int Unreference()
	{
		references--;
		return references;
	}
#endif

	// Necessary evil to deal with SmartPtr<const T> pointers
	void Reference() const
	{
		const_cast< SmartObject* > ( this )->Reference();
	}
	unsigned int Unreference() const
	{
		return const_cast< SmartObject* > ( this )->Unreference();
	}

	unsigned int references;

    template<class U> friend class SmartPtr;

public:
    ///@cond INTERNAL
    /**
     * Destructor.
     */
#if defined(_DEBUG_) && _SMARTPTR_DEBUG_ == 1
    virtual ~SmartObject();
#else
    virtual ~SmartObject() {}
#endif

    /**
     * Gets the number of current references to this instance.
     */
    unsigned int GetReferences() const
    {
        return references;
    }
    ///@endcond

protected:
    /**
     * Default constructor.
     *
     * @remarks
     * The default constructor is protected to prevent the instantation of objects of this class
     * (because it's abstract)
     */
#if defined(_DEBUG_) && _SMARTPTR_DEBUG_ == 1
    SmartObject();
#else
	SmartObject() : references( 0 )	{}
#endif
};


/**
 * The SmartPtr template class is used to dynamically manage @link SmartObject Smart Objects@endlink.
 *
 * SmartPtr instances behave similar to standard pointers, by storing a reference (pointer) to a smart
 * object.
 *
 * The difference with standard pointers is that the objects referenced by smart pointers are
 * dynamically managed, such that when the last smart pointer referencing an object is deleted, that
 * object is also deleted.
 */
template< class T >
class SmartPtr
{
public:
    /**
     * Default constructor, creates a new "null" smart pointer that doesn't point to any object.
     */
	SmartPtr() :
		ptr( NULL )
	{
	}

    /**
     * Destructor.
     *
     * The pointed object reference count is decremented by one. If that reference count drops
     * to zero (i.e. this was the last referencing smart pointer), then the pointed object is deleted.
     */
	~SmartPtr()
	{
		Unreference();
	}

    /**
     * Creates a new smart pointer to the (smart) object pointed by \a o.
     *
     * The pointed object reference count is incremented by one.
     *
     * @remark
     * The object \a o must have been created on the heap (i.e. using the operator \a new).
     * Objects created on the stack (i.e. using automatic variables) can't be dynamically
     * deleted, and therefore a runtime error will be thrown when the last smart pointer to
     * it tries to delete it.
     *
     * @remark
     * \p NULL can be passed in \a o to create a "null" smart pointer (like SmartPtr::SmartPtr()).
     */
	SmartPtr( T* o )
	{
		Reference( o );
	}

	/**
     * Copy constructor, creates a new smart pointer from another one.
     *
     * The pointed object reference count is incremented by one.
     */
	SmartPtr( const SmartPtr<T>& sp )
	{
		Reference( sp.ptr );
	}

    /**
     * Copy constructor, creates a new smart pointer from a smart pointer to an object of different
     * (castable) class.
     *
     * The pointed object reference count is incremented by one.
     *
     * @remarks
     * If the pointed object class can't be casted, a compile-time error will be raised.
     */
	template< class T_CF >
	SmartPtr( const SmartPtr<T_CF>& sp )
	{
		Reference( sp.operator->() );
	}

    ///@name Operators
	///@{

	/**
	 * Assignment operator, sets the smart pointer to point to the object pointed by \a o.
	 *
     * The currently pointed object reference count is decremented by one. If that reference count drops
     * to zero (i.e. this was the last referencing smart pointer), then the pointed object is deleted.
     *
     * The newly pointed object (\a o) reference count is incremented by one.
     *
     * @remark
     * \p NULL can be passed in \a o to deference the currently pointed object and make the smart pointer
     * become "null".
	 */
	SmartPtr<T>& operator=( T* o )
	{
		Unreference();
		Reference( o );
		return *this;
	}

    /**
     * Assignment operator, sets the smart pointer to point to the object pointed by \a sp.
     * @see SmartPtr::operator=(T*)
     */
	SmartPtr<T>& operator=( const SmartPtr<T>& sp )
	{
		Unreference();
		Reference( sp.ptr );
		return *this;
	}

    /**
     * Assignment operator.
     */
	template< class T_CF >
	SmartPtr<T>& operator=( const SmartPtr<T_CF>& sp )
	{
		Unreference();
		Reference( sp.operator->() );
		return *this;
	}

    /**
     * Derefence operator, used to access the pointed object methods and attributes.
     * @return A pointer to the pointed object
     */
	T* operator->() const
	{
		return ptr;
	}

    /**
     * Indirection operator.
     * @return A reference to the pointed object
     */
	T& operator*() const
	{
		return *ptr;
	}

    /**
     * Unwrapping operator.
     * @return A (standard) pointer to the pointed object
     */
    T* operator~() const
    {
        return ptr;
    }

    /**
     * Equality comparison operator.
     * @return \p True if both this smart pointer and \a sp point to the same instance, \p false otherwise
     */
	bool operator==( const SmartPtr<T>& sp ) const
	{
		return ptr == sp.ptr;
	}

    /**
     * Equality comparison operator.
     * @return \p True if both this smart pointer and \a sp point to the same instance, \p false otherwise
     */
	template< class T_CF >
	bool operator==( const SmartPtr<T_CF>& sp ) const
	{
		return ptr == sp.operator->();
	}

    /**
     * Non-equality comparison operator.
     * @return \p True if this smart pointer and \a sp point to different instances, \p false otherwise
     */
	bool operator!=( const SmartPtr<T>& sp ) const
	{
		return ptr != sp.ptr;
	}

    /**
     * Non-equality comparison operator.
     * @return \p True if this smart pointer and \a sp point to different instances, \p false otherwise
     */
	template< class T_CF >
	bool operator!=( const SmartPtr<T_CF>& sp ) const
	{
		return ptr != sp.operator->();
	}

    /**
     * Checks whether the smart pointer points to an object.
     * @return \p True if the smart pointer points to an object, \p false otherwise
     * @see IsValid()
     */
    operator bool() const
    {
        return (ptr != NULL);
    }

    ///@}

	/**
	 * Checks whether the smart pointer doesn't point to any object.
	 * @return \p True if the smart pointer doesn't point to an object, \p false otherwise
	 */
	bool IsNull() const
	{
		return (ptr == NULL);
	}

    /**
     * Checks whether the smart pointer points to an object.
     * @return \p True if the smart pointer points to an object, \p false otherwise
     */
	bool IsValid() const
	{
		return (ptr != NULL);
	}

	/**
	 * Creates a new smart pointer from a pointer to an object of a different class,
	 * by performing a dynamic (runtime) cast of the pointed instance.
	 */
	template< class T_CF >
	static SmartPtr<T> cast_dynamic( T_CF* o )
	{
		return SmartPtr<T> ( dynamic_cast< T* > ( o ) );
	}

    /**
     * Creates a new smart pointer from a smart pointer to an object of a different class,
     * by performing a dynamic (runtime) cast of the pointed instance.
     */
	template< class T_CF >
	static SmartPtr<T> cast_dynamic( const SmartPtr<T_CF>& sp )
	{
		return cast_dynamic( sp.operator->() );
	}

    /**
     * Creates a new smart pointer from a pointer to an object of a different class,
     * by performing a static (compile-time) cast of the pointed instance.
     */
	template< class T_CF >
	static SmartPtr<T> cast_static( T_CF* o )
	{
		return SmartPtr<T> ( static_cast< T* > ( o ) );
	}

    /**
     * Creates a new smart pointer from a smart pointer to an object of a different class,
     * by performing a static (compile-time) cast of the pointed instance.
     */
	template< class T_CF >
	static SmartPtr<T> cast_static( const SmartPtr<T_CF>& sp )
	{
		return cast_static( sp.operator->() );
	}

    /**
     * Creates a new smart pointer from a pointer to an object,
     * casting away constness or volatility of the pointed instance.
     */
	template< class T_CF >
	static SmartPtr<T> cast_const( T_CF* o )
	{
		return SmartPtr<T> ( const_cast< T* > ( o ) );
	}

    /**
     * Creates a new smart pointer from a smart pointer to an object,
     * casting away constness or volatility of the pointed instance.
     */
	template< class T_CF >
	static SmartPtr<T> cast_const( const SmartPtr<T_CF>& sp )
	{
		return cast_const( sp.operator->() );
	}

private:
	T* ptr;

	void Reference( T* o )
	{
		ptr = o;
		if( ptr ) ptr->Reference();
	}

	void Unreference()
	{
		if( ptr )
		{
		    if( !ptr->Unreference() ) delete ptr;
		}
	}
};

///@}

#endif //_SMARTLIB_SMARTPTR_H_
